package com.xbongbong.paas.service.abstracts;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.xbongbong.paas.constant.PaasConstant;
import com.xbongbong.paas.domain.entity.PaasFormExplainEntity;
import com.xbongbong.paas.field.FieldAttrEntity;
import com.xbongbong.paas.model.PaasFormExplainModel;
import com.xbongbong.paas.redis.PaasRedisHelper;
import com.xbongbong.paas.redis.RedisPrefixConstant;
import com.xbongbong.paas.service.PaasRepairExplainService;
import com.xbongbong.paas.toolbox.exception.XbbException;
import com.xbongbong.paas.toolbox.util.DateTimeUtil;
import com.xbongbong.paas.util.ExplainUtil;
import com.xbongbong.pro.config.ProBaseConfig;
import com.xbongbong.pro.enums.ErrorCodeEnum;
import com.xbongbong.pro.enums.RepairExplainEnum;
import com.xbongbong.pro.enums.errorcodes.SystemErrorCodeEnum;
import com.xbongbong.pro.script.pojo.dto.AllScriptDTO;
import com.xbongbong.pro.script.pojo.vo.ScriptVO;
import com.xbongbong.saas.enums.XbbRefTypeEnum;
import com.xbongbong.saas.script.help.SaasInitExplainsHelper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * @author 吴峰
 * @date 2023/02/06 10:50
 */
@Slf4j
@Component
public abstract class AbstractPaasRepairExplainService implements PaasRepairExplainService {

    @Resource
    private PaasFormExplainModel paasFormExplainModel;
    @Resource
    private PaasRedisHelper paasRedisHelper;
    @Resource
    private ProBaseConfig proBaseConfig;

    @Override
    public ScriptVO repairExplain(AllScriptDTO allScriptDTO) throws XbbException {
        if (!proBaseConfig.getXbbCode().equals(allScriptDTO.getXbbCode())) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100006, SystemErrorCodeEnum.API_ERROR_100006.getMsg());
        }
        long start = DateTimeUtil.getInt();
        Integer businessType = allScriptDTO.getBusinessType();
        String fieldAttr = allScriptDTO.getFieldAttr();
        boolean updateFieldAttr = !fieldAttr.isEmpty();
        Class<?> clazz = getBusinessClass(businessType);
        Class<?> tempClazz = getBusinessTempClass(businessType);
        List<String> fieldAttrList = SaasInitExplainsHelper.getBusinessEnumAttrs(clazz);
        if (updateFieldAttr && (!fieldAttrList.contains(fieldAttr) || Objects.equals(fieldAttr,"attr"))){
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001,"非法的字段属性");
        }
        RepairExplainEnum repairExplainEnum = RepairExplainEnum.getByCode(allScriptDTO.getOperate());
        List<String> corpidList = allScriptDTO.getCorpidList();
        if (corpidList.isEmpty()){
            corpidList = paasFormExplainModel.getCorpidList();
            corpidList.add("0");
        }
        allScriptDTO.setCorpidList(corpidList);
        if (Objects.equals(businessType, XbbRefTypeEnum.WORK_ORDER.getCode())) {

        }else {
            switch (repairExplainEnum){
                case REPAIR:
                    repairSaasExplain(allScriptDTO, clazz, tempClazz);
                    break;
                case DEL:
                    delSaasExplain(allScriptDTO, clazz, tempClazz);
                    break;
                case ADD:
                    addSaasExplain(allScriptDTO, clazz, tempClazz);
                    break;
                default:
                    throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001,"未知操作");
            }

        }
        String key = PaasConstant.DEFAULT_DATA_CORPID + "_" + businessType;
        paasRedisHelper.removeValue(RedisPrefixConstant.FIELD_EXPLANATION, key);
        log.warn("consume:" + (DateTimeUtil.getInt() - start));
        return null;
    }

    public void addSaasExplain(AllScriptDTO allScriptDTO, Class<?> clazz, Class<?> tempClazz) throws XbbException{
        String attr = allScriptDTO.getAttr();
        String afterAttr = allScriptDTO.getAfterAttr();
        Integer businessType = allScriptDTO.getBusinessType();
        boolean insertFieldAttr = !attr.isEmpty();
        log.warn(allScriptDTO.getCorpidList().size() + "家公司");
        Integer index = 1;
        List<Long> failList = new ArrayList<>();
        Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        param.put("del", 0);
        param.put("businessType", businessType);
        if (Objects.nonNull(allScriptDTO.getDistributorMark())) {
            param.put("distributorMark", allScriptDTO.getDistributorMark());
        }
        for (String corpid : allScriptDTO.getCorpidList()) {
            log.warn(index + "/" + allScriptDTO.getCorpidList().size());
            index++;
            param.put("corpid", corpid);
            if (allScriptDTO.getFormId() != null) {
                param.put("formId", allScriptDTO.getFormId());
            }
            List<PaasFormExplainEntity> paasFormExplainEntityList = paasFormExplainModel.getNoHandleExplainsByFindEntitys(param);
            Map<String, Object> enumMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            Object[] objs = clazz.getEnumConstants();
            for (Object obj : objs) {
                try {
                    String name = (String) clazz.getMethod("getAttr").invoke(obj);
                    enumMap.put(name, obj);
                } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
                    e.printStackTrace();
                }
            }
            //思路: 将写好的枚举 分别装入 fieldAttrEntity
            for(PaasFormExplainEntity entity : paasFormExplainEntityList) {
                boolean insert = false;
                List<FieldAttrEntity> fieldAttrEntityList = JSONArray.parseArray(entity.getExplains(),FieldAttrEntity.class);
                Map<String, FieldAttrEntity> explainMap = ExplainUtil.getExplainMap(entity.getExplains(), null);
                if (!explainMap.containsKey(attr)) {
                    for(int i = 0; i < fieldAttrEntityList.size(); i++){
                        //插入到当前字段后面
                        FieldAttrEntity fieldAttrEntity = fieldAttrEntityList.get(i);
                        if(Objects.equals(afterAttr,fieldAttrEntity.getAttr())){
                            if(insertFieldAttr){
                                try {
                                    // 包装业务枚举类;
                                    List<String> enumAttrs = null;
                                    if (enumAttrs == null) {
                                        // 获取业务枚举类中的属性，以集合的形式返回，同一个枚举的属性值只获取一次
                                        enumAttrs = SaasInitExplainsHelper.getBusinessEnumAttrs(clazz);
                                    }
                                    Object obj = enumMap.get(attr);
                                    // 初始化字段解释
                                    FieldAttrEntity newFieldAttrEntity = SaasInitExplainsHelper.setField(clazz, enumAttrs,obj);
                                    if (newFieldAttrEntity != null) {
                                        String saasAttr = newFieldAttrEntity.getSaasAttr();
                                        String initAttr = saasAttr.substring(0, 1).toUpperCase() + saasAttr.substring(1);
                                        String methodName = "init" + initAttr;
                                        try {
                                            //这里拿到了新加入字段的初始化方法 并执行它 参数为 他的实例
                                            Method methodHasParameter = tempClazz.getMethod(methodName, FieldAttrEntity.class);
                                            methodHasParameter.invoke(tempClazz, newFieldAttrEntity);
                                        } catch (NoSuchMethodException e) {
                                            // 没有对应字段的初始化方法
                                        } catch (Exception e) {
                                            log.warn(e.getMessage());
                                            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, SystemErrorCodeEnum.API_ERROR_100001.getMsg());
                                        }
                                        fieldAttrEntityList.add(i + 1,newFieldAttrEntity);
                                        entity.setExplains(JSONObject.toJSONString(fieldAttrEntityList));
                                        paasFormExplainModel.update(entity);
                                    }
                                } catch (Exception e) {
                                    failList.add(entity.getId());
                                    e.printStackTrace();
                                }

                            }
                        }
                    }
                }
            }
        }
        if (failList.isEmpty()) {
            throw new XbbException(ErrorCodeEnum.API_SUCCESS, "更新成功");
        } else {
            throw new XbbException(ErrorCodeEnum.API_SUCCESS, "部分更新成功，id " + failList + "更新失败");
        }
    }

    /**
     * saas解释修复
     * @param allScriptDTO 入参
     * @param clazz 主枚举
     * @param tempClazz 辅助枚举
     * @throws XbbException
     */
    public void repairSaasExplain(AllScriptDTO allScriptDTO, Class<?> clazz, Class<?> tempClazz) throws XbbException {
        String attr = allScriptDTO.getAttr();
        String fieldAttr = allScriptDTO.getFieldAttr();
        String initMethod = allScriptDTO.getInitMethod();
        String initMethodField = allScriptDTO.getInitMethodField();
        boolean updateFieldAttr = !fieldAttr.isEmpty();
        boolean updateFieldAttrList = CollectionUtils.isNotEmpty(allScriptDTO.getFieldAttrList());
        boolean updateInitMethod = !initMethod.isEmpty();
        Integer businessType = allScriptDTO.getBusinessType();
        log.warn(allScriptDTO.getCorpidList().size() + "家公司");
        Integer index = 1;
        List<Long> failList = new ArrayList<>();
        for (String corpid : allScriptDTO.getCorpidList()) {
            log.warn(index + "/" + allScriptDTO.getCorpidList().size());
            index++;
            Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            param.put("corpid", corpid);
            param.put("del", 0);
            param.put("businessType", businessType);
            if(allScriptDTO.getFormId() !=null) {
                param.put("formId", allScriptDTO.getFormId());
            }
            if (Objects.nonNull(allScriptDTO.getDistributorMark())) {
                param.put("distributorMark", allScriptDTO.getDistributorMark());
            }
            List<PaasFormExplainEntity> paasFormExplainEntityList = paasFormExplainModel.getNoHandleExplainsByFindEntitys(param);
            List<PaasFormExplainEntity> updateList = new ArrayList<>();
            Map<String, Object> enumMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            Object[] objs = clazz.getEnumConstants();
            for (Object obj : objs) {
                try {
                    String name = (String) clazz.getMethod("getAttr").invoke(obj);
                    enumMap.put(name, obj);
                } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
                    e.printStackTrace();
                }
            }
            for (PaasFormExplainEntity entity : paasFormExplainEntityList) {
                boolean update = false;
                List<FieldAttrEntity> fieldAttrEntityList = JSONArray.parseArray(entity.getExplains(), FieldAttrEntity.class);
                for (FieldAttrEntity fieldAttrEntity : fieldAttrEntityList) {
                    if (Objects.equals(attr, fieldAttrEntity.getAttr())) {
                        if (updateFieldAttr) {
                            String fieldAttrInvoke = fieldAttr.substring(0, 1).toUpperCase() + fieldAttr.substring(1);
                            try {
                                if (!enumMap.containsKey(attr)) {
                                    continue;
                                }
                                Object value = clazz.getMethod("get" + fieldAttrInvoke).invoke(enumMap.get(attr));
                                Class<?> returnType = clazz.getMethod("get" + fieldAttrInvoke).getReturnType();
                                Method method = FieldAttrEntity.class.getMethod("set" + fieldAttrInvoke, returnType);
                                method.invoke(fieldAttrEntity, value);
                            } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
                                e.printStackTrace();
                            }
                            update = true;
                        } else if (updateFieldAttrList) {
                            for (String updateField : allScriptDTO.getFieldAttrList()) {
                                String fieldAttrInvoke = updateField.substring(0, 1).toUpperCase() + updateField.substring(1);
                                try {
                                    if (!enumMap.containsKey(attr)) {
                                        continue;
                                    }
                                    Object value = clazz.getMethod("get" + fieldAttrInvoke).invoke(enumMap.get(attr));
                                    Class<?> returnType = clazz.getMethod("get" + fieldAttrInvoke).getReturnType();
                                    Method method = FieldAttrEntity.class.getMethod("set" + fieldAttrInvoke, returnType);
                                    method.invoke(fieldAttrEntity, value);
                                } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
                                    e.printStackTrace();
                                }
                            }
                            update = true;
                        }
                        if (updateInitMethod) {
                            try {
                                Method method = tempClazz.getMethod("init" + initMethod.substring(0, 1).toUpperCase() + initMethod.substring(1), FieldAttrEntity.class);
                                SaasInitExplainsHelper.commonFiledInit(fieldAttrEntity);
                                FieldAttrEntity initEntity = new FieldAttrEntity();
                                initEntity.setFieldType(fieldAttrEntity.getFieldType());
                                method.invoke(tempClazz, initEntity);
                                Method method1 = FieldAttrEntity.class.getMethod("get" + initMethodField.substring(0, 1).toUpperCase() + initMethodField.substring(1));
                                Object value = method1.invoke(initEntity);
                                Class<?> returnType = FieldAttrEntity.class.getMethod("get" + initMethodField.substring(0, 1).toUpperCase() + initMethodField.substring(1)).getReturnType();
                                Method method2 = FieldAttrEntity.class.getMethod("set" + initMethodField.substring(0, 1).toUpperCase() + initMethodField.substring(1), returnType);
                                method2.invoke(fieldAttrEntity, value);
                            } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
                                e.printStackTrace();
                            }
                            update = true;
                        }
                        if (!updateFieldAttr && !updateInitMethod && !updateFieldAttrList) {
                            try {
                                String saasAttr = fieldAttrEntity.getSaasAttr();
                                Method method = tempClazz.getMethod("init" + saasAttr.substring(0, 1).toUpperCase() + saasAttr.substring(1), FieldAttrEntity.class);
                                method.invoke(tempClazz, fieldAttrEntity);
                            } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
                                e.printStackTrace();
                            }
                            update = true;
                        }
                        break;
                    }
                }
                if (update) {
                    entity.setExplains(JSONArray.toJSONString(fieldAttrEntityList));
                    updateList.add(entity);
                } else {
                    failList.add(entity.getId());
                }
            }
            if (updateList.isEmpty()) {
//                throw new XbbException(ErrorCodeEnum.API_SUCCESS, "未找到更新内容");
            } else {
                for (PaasFormExplainEntity entity : updateList) {
                    paasFormExplainModel.update(entity);
                }
            }
        }
        if (failList.isEmpty()) {
            throw new XbbException(ErrorCodeEnum.API_SUCCESS, "更新成功");
        } else {
            throw new XbbException(ErrorCodeEnum.API_SUCCESS, "部分更新成功，id " + failList + "更新失败");
        }
    }

    /**
     * saas解释删除
     * @param allScriptDTO 入参
     * @param clazz 主枚举
     * @param tempClazz 辅助枚举
     * @throws XbbException
     */
    public void delSaasExplain(AllScriptDTO allScriptDTO, Class<?> clazz, Class<?> tempClazz) throws XbbException {
        String attr = allScriptDTO.getAttr();
        Integer businessType = allScriptDTO.getBusinessType();
        Map<String,Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        param.put("corpidIn",allScriptDTO.getCorpidList());
        param.put("del",0);
        param.put("businessType",businessType);
        if (Objects.nonNull(allScriptDTO.getDistributorMark())) {
            param.put("distributorMark", allScriptDTO.getDistributorMark());
        }
        List<PaasFormExplainEntity> paasFormExplainEntityList = paasFormExplainModel.getNoHandleExplainsByFindEntitys(param);
        List<PaasFormExplainEntity> updateList = new ArrayList<>();
        List<Long> failList = new ArrayList<>();
        Integer index = 1;
        for (PaasFormExplainEntity entity : paasFormExplainEntityList){
            log.info(index + "/" + paasFormExplainEntityList.size());
            index ++;
            boolean update = false;
            List<FieldAttrEntity> fieldAttrEntityList = JSONArray.parseArray(entity.getExplains(),FieldAttrEntity.class);
            Iterator<FieldAttrEntity> iterator = fieldAttrEntityList.iterator();
            while (iterator.hasNext()){
                FieldAttrEntity fieldAttrEntity = iterator.next();
                if (Objects.equals(fieldAttrEntity.getAttr(),attr) && Objects.equals(fieldAttrEntity.getIsRedundant(),0)){
                    iterator.remove();
                    update = true;
                }
            }
            if (update){
                entity.setExplains(JSONArray.toJSONString(fieldAttrEntityList));
                updateList.add(entity);
            }else {
                failList.add(entity.getId());
            }
        }
        if (updateList.isEmpty()){
            throw new XbbException(ErrorCodeEnum.API_SUCCESS,"未找到删除内容");
        }else {
            for (PaasFormExplainEntity entity : updateList){
                paasFormExplainModel.update(entity);
            }
            if (failList.isEmpty()){
                throw new XbbException(ErrorCodeEnum.API_SUCCESS,"更新成功");
            }else {
                throw new XbbException(ErrorCodeEnum.API_SUCCESS,"部分更新成功，id " + failList + "更新失败");
            }
        }
    }

}
